/*-Limits.---------------------------------------------------------------------------------------*/
#define MAX_VERTEXES_MODEL 10000
#define MAX_POLYGONS_PER_MODEL 10000
#define MAX_MESHES_PER_MODEL 100
#define MAX_VERTEXES_PER_MESH 10000
#define MAX_VECTORS_PER_MESH 10000
#define MAX_POLYGONS_PER_MESH 10000

/*-Structures.-----------------------------------------------------------------------------------*/
// VERTEX is a corner of a polygon.
typedef struct
{
  float x, y, z;       // position
  float u, w;          // Texture coordinates.
  BYTE flag;
} VERTEX;

// Trianglar polygon of a 3d model. Unlike TRIANLE this one oses vertexes.
typedef struct
{
  VERTEX *vertex[3];             // Pointers corner vertexes
  IJK normal;                    // Normal of the polygon surface
  IJK *vector[3];                // Pointers to smoothshading normals.
} POLYGON;

// Mesh of a 3D-model
typedef struct
{
  VERTEX *vertex;                    // Pointer to vertex list.
  POLYGON *polygon;                  // Pointer to polygon list.
  IJK *vector;                       // Pointer to vector list
  int number_of_vertexes;            // Number of vertexes in mesh
  int number_of_polygons;            // Number of polygons in mesh
  int number_of_vectors;             // Number of vectors in mesh
  int fins;
} MESH;

// MODEL is a 3D model.
typedef struct
{
  int number_of_meshes;    // Number of meshes in this model
  int id;                  // Display list id
  MESH *mesh;              // Pointer to mesh list
} MODEL;


/*-----------------------------------calculates normal for a polygon.----------------------------*/
void calculate_polygon_normal(POLYGON *p)
{
  /*----------------------------------Locals.----------------------------------------------------*/
  IJK v1, v2;                     // Two vectors on the surface.
  float lenght;                   // Lenght of the normal.

  /*---------------Greate two vectors on the surface.--------------------------------------------*/
  v1.i=p->vertex[1]->x-p->vertex[0]->x;
  v1.j=p->vertex[1]->y-p->vertex[0]->y;
  v1.k=p->vertex[1]->z-p->vertex[0]->z;
  v2.i=p->vertex[2]->x-p->vertex[0]->x;
  v2.j=p->vertex[2]->y-p->vertex[0]->y;
  v2.k=p->vertex[2]->z-p->vertex[0]->z;

  /*-----------------Generate the normal.--------------------------------------------------------*/
  cross_product(&v1, &v2, &p->normal);
  normalize_vector(&p->normal);
}


/*-----------------------Initializes a 3D model. Does not free the memory.-----------------------*/
void init_model(MODEL *m)
{
  m->number_of_meshes=0;   // No meshes
  m->id=0;                 // No list
  m->mesh=NULL;            // No mesh list
}


/*---------------------Frees all memory allocated by a 3D model.---------------------------------*/
void free_model(MODEL *m)
{
  /*-------------------------Locals.-------------------------------------------------------------*/
  int i;


  /*-------------------------Delete meshes.------------------------------------------------------*/
  if (m->mesh)
  {
    for (i=0; i<m->number_of_meshes; i++)
    {
      if (m->mesh[i].vertex) free(m->mesh[i].vertex);
      if (m->mesh[i].polygon) free(m->mesh[i].polygon);
      if (m->mesh[i].vector) free(m->mesh[i].vector);
    }
    free(m->mesh);
  }


  /*----------------------------Delete the display lists.----------------------------------------*/
  if (m->id>0) glDeleteLists(m->id, m->number_of_meshes);


  /*---------------------------------Zero the number of meshes.----------------------------------*/
  m->number_of_meshes=0;
}


/*-----------------------Initializes a VERTEX structure.-----------------------------------------*/
void init_mesh(MESH *m)
{
  m->vertex=NULL;                     // Pointer to vertex list.
  m->polygon=NULL;                    // Pointer to polygon list.
  m->vector=NULL;                     // Pointer to vector list
  m->number_of_vertexes=0;            // Number of vertexes in mesh
  m->number_of_polygons=0;            // Number of polygons in mesh
  m->number_of_vectors=0;             // Number of vectors in mesh
}


/*----------------------Builds a displaylist froma model.----------------------------------------*/
void build_displaylist(MODEL *m)
{
  /*---------------------------------Locals.-----------------------------------------------------*/
  int i, ii, mesh;

  /*---------------------Get list id.------------------------------------------------------------*/
  m->id=glGenLists(m->number_of_meshes);


  /*--------------------Build the list.----------------------------------------------------------*/
  for (mesh=0; mesh<m->number_of_meshes; mesh++)
  {
    glNewList(m->id+mesh, GL_COMPILE);
    glBegin(GL_TRIANGLES);
    for (i=0; i<m->mesh[mesh].number_of_polygons; i++)
    {
      for (ii=0; ii<3; ii++)
      {
        glNormal3f(m->mesh[mesh].polygon[i].vector[ii]->i,
                   m->mesh[mesh].polygon[i].vector[ii]->j,
                   m->mesh[mesh].polygon[i].vector[ii]->k);
        //glTexCoord2f(m->mesh[mesh].polygon[i].vertex[ii]->u,
        //             m->mesh[mesh].polygon[i].vertex[ii]->w);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, m->mesh[mesh].polygon[i].vertex[ii]->u,
                                              m->mesh[mesh].polygon[i].vertex[ii]->w);
		glVertex3f(m->mesh[mesh].polygon[i].vertex[ii]->x,
                   m->mesh[mesh].polygon[i].vertex[ii]->y,
                   m->mesh[mesh].polygon[i].vertex[ii]->z);
      }
    }
    glEnd();
    glEndList();
  }
}


/*-------------------------Loads "MilkShape 3d ASCII"-file.--------------------------------------*/
BOOL load_milkshape_model(MODEL *m, char *filename)
{
  /*------------------------------Locals.--------------------------------------------------------*/
  FILE *file;
  char buffer[80];
  int i, ii, mesh, temp, temp2;
  int v1, v2, v3, n1, n2, n3;
  int check;


  /*-----------------------------Try to open the file.-------------------------------------------*/
  file=fopen(filename, "r");
  if (!file)
  {
    sprintf(buffer, "Can't open file: %s", filename);
	MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
  }
  init_model(m);


  /*-------------------------------------Skip the first lines.-----------------------------------*/
  fgets(buffer, 80, file);                             // // MILKSHAPE 3D ASCII
  fgets(buffer, 80, file);                             // Empty line
  fgets(buffer, 80, file);                             // Frames:
  fgets(buffer, 80, file);                             // Frame:
  fgets(buffer, 80, file);                             // Empty line


  /*---------------------------Read the number of meshes.----------------------------------------*/
  fgets(buffer, 80, file);                                 // Meshes: number_of_meshes
  check=sscanf(buffer, "Meshes: %d", &m->number_of_meshes);
  if (m->number_of_meshes<=0||m->number_of_meshes>=MAX_MESHES_PER_MODEL||check!=1)
  {
    sprintf(buffer, "Bad number of meshes in file %s.", filename);
	MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONINFORMATION);
	fclose(file);
    return FALSE;
  }


  /*-----------------------------Allocate mamory for meshes.-------------------------------------*/
  m->mesh=(MESH *)malloc(m->number_of_meshes*sizeof(MESH));         // Allocate memory.
  if (!m->mesh)                                                     // Succes?
  {
    fclose(file);
    free_model(m);
    sprintf(buffer, "Can't allocate memory for meshes in file %s.", filename);
    MessageBox(NULL,buffer, "ERROR",MB_OK|MB_ICONINFORMATION);
    return FALSE;
  }
  for (mesh=0; mesh<m->number_of_meshes; mesh++) init_mesh(&m->mesh[mesh]);


  /*--------------------------------------------------Loop meshes--------------------------------*/
  for (mesh=0; mesh<m->number_of_meshes; mesh++)
  {
    fgets(buffer, 80, file);                 // "name" 0 material

	// Read the number of vertexes
    fgets(buffer, 79, file);                                 // Number_of_vertexes in this mesh.
    check=sscanf(buffer, "%d", &m->mesh[mesh].number_of_vertexes); // Read number of vertexes
    if (m->mesh[mesh].number_of_vertexes<=0||
        m->mesh[mesh].number_of_vertexes>MAX_VERTEXES_PER_MESH||check!=1)
    {
      fclose(file);
      free_model(m);
      sprintf(buffer, "Bad number of vertices in %dth mesh in file %s", mesh+1, filename);
      MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONINFORMATION);
      return FALSE;
    }

    // Allocate memory for vertexes.
    m->mesh[mesh].vertex=(VERTEX *)malloc(m->mesh[mesh].number_of_vertexes*sizeof(VERTEX));
    if (!m->mesh[mesh].vertex)
    {
	  fclose(file);
      free_model(m);
      sprintf(buffer, "Can't allocate memory for vertices in %dth mesh in file %s.", mesh+1, filename);
	  MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONINFORMATION);
	  return FALSE;
    }

    // Read vertexes.
    for (i=0; i<m->mesh[mesh].number_of_vertexes; i++)
    {
      fgets(buffer, 80, file);
      check=sscanf(buffer, "%d %f %f %f %f %f",
                   &temp,
                   &m->mesh[mesh].vertex[i].x,
                   &m->mesh[mesh].vertex[i].y,
                   &m->mesh[mesh].vertex[i].z,
                   &m->mesh[mesh].vertex[i].u,
                   &m->mesh[mesh].vertex[i].w);
      if (check!=6)
      {
        fclose(file);
        free_model(m);
        sprintf(buffer, "%dth vertex in %dth mesh in file %s is invalid!", i+1, mesh+1, filename);
        MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONINFORMATION);
        return FALSE;
      }
      m->mesh[mesh].vertex[i].flag=0;
    }

    // Read number of smoothshade normals
    fgets(buffer, 80, file);                                 // Number of normals
    check=sscanf(buffer, "%d", &m->mesh[mesh].number_of_vectors);  // read number of normals
    if (m->mesh[mesh].number_of_vectors<=0||
        m->mesh[mesh].number_of_vectors>MAX_VECTORS_PER_MESH||check!=1)
    {
      fclose(file);
      free_model(m);
      sprintf(buffer, "Bad number of normals in %dth mesh in file: %s", mesh+1, filename);
      MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONINFORMATION);
      return FALSE;
    }

	// Allocate memory for vectors.
    m->mesh[mesh].vector=(IJK *)malloc(m->mesh[mesh].number_of_vectors*sizeof(IJK));
    if (!m->mesh[mesh].vector)
    {
      fclose(file);
      free_model(m);
      sprintf(buffer, "Can't allocate memory for normals in %dth mesh in file %s.", mesh+1, filename);
      MessageBox(NULL,buffer, "ERROR",MB_OK|MB_ICONINFORMATION);
      return FALSE;
    }

	// Read the vectors
	for (i=0; i<m->mesh[mesh].number_of_vectors; i++)
    {
      fgets(buffer, 80, file);
      check=sscanf(buffer,"%f %f %f",
                   &m->mesh[mesh].vector[i].i,
                   &m->mesh[mesh].vector[i].j,
                   &m->mesh[mesh].vector[i].k);
      if (check!=3)
      {
        fclose(file);
        free_model(m);
        sprintf(buffer, "%dth vector in %dth mesh in file %s is invalid!", i+1, mesh+1, filename);
        MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONINFORMATION);
        return FALSE;
      }
    }

	// Read the number of polygons
    fgets(buffer, 80, file);                                  // Number of polygons
    check=sscanf(buffer, "%d", &m->mesh[mesh].number_of_polygons);
    if (m->mesh[mesh].number_of_polygons<=0||
        m->mesh[mesh].number_of_polygons>=MAX_POLYGONS_PER_MESH||check!=1)
    {
      sprintf(buffer, "Bad number of polygons in %dth mesh in file %s", mesh+1, filename);
      MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONINFORMATION);
      fclose(file);
      return FALSE;
    }

	// Allocate memory for polygons
    m->mesh[mesh].polygon=(POLYGON *)malloc(m->mesh[mesh].number_of_polygons*sizeof(POLYGON));
    if (!m->mesh[mesh].polygon)
    {
      sprintf(buffer,"Can't allocate memory for polygons in file %s.", filename);
	  MessageBox(NULL,buffer,"ERROR",MB_OK|MB_ICONINFORMATION);
	  fclose(file);
      return FALSE;
    }

	// Read the polygons
    for (i=0; i<m->mesh[mesh].number_of_polygons; i++)
    {
      fgets(buffer, 80, file);        // flag, vertex 1, 2 and 4 Normal 1, 2 and 3
      check=sscanf(buffer, "%d %d %d %d %d %d %d", &temp, &v1, &v2, &v3, &n1, &n2, &n3);

      if (v1<0||v2<0||v3<0||n1<0||n2<0||n3<0||check!=7)
      {
        fclose(file);
        free_model(m);
        sprintf(buffer, "Error in %dth polygon of %dth mesh in file %s.", i+1, mesh+1, filename);
	    MessageBox(NULL, buffer, "ERROR", MB_OK|MB_ICONINFORMATION);
		return FALSE;
      }

      m->mesh[mesh].polygon[i].vertex[0]=&m->mesh[mesh].vertex[v1];
      m->mesh[mesh].polygon[i].vertex[1]=&m->mesh[mesh].vertex[v2];
      m->mesh[mesh].polygon[i].vertex[2]=&m->mesh[mesh].vertex[v3];
      m->mesh[mesh].polygon[i].vector[0]=&m->mesh[mesh].vector[n1];
      m->mesh[mesh].polygon[i].vector[1]=&m->mesh[mesh].vector[n2];
      m->mesh[mesh].polygon[i].vector[2]=&m->mesh[mesh].vector[n3];

	  calculate_polygon_normal(&m->mesh[mesh].polygon[i]);
	}
  }

  build_displaylist(m);

  /*----------------------------------------Everything OK.---------------------------------------*/
  fclose(file);
  return TRUE;
}
